/*
 * mod_fastdfs.c
 *
 *  Created on: 2011-4-5
 *      Author: saint
 */
#include <ctype.h>
#include <stdlib.h>
#include <stdio.h>
#define _GNU_SOURCE
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <sys/mman.h>
#include <stdint.h>
#include <fdfs_client.h>
#include <magick/MagickCore.h>
#include "base.h"
#include <logger.h>
#include "log.h"
#include "buffer.h"
#include "plugin.h"
#include "stat_cache.h"
#include "etag.h"
#include "response.h"
#include "status_counter.h"
#include "version.h"

#ifdef LIGHTTPD_V14
#include "splaytree.h"
#endif

#define CONFIG_FASTDFS_CLIENT_CONF "fastdfs.conf"
#define CONFIG_FASTDFS_CONVERT_IMG_TYPES "fastdfs.convert-filetypes"
#define CONFIG_FASTDFS_CONVERT_IMG_SIZE "fastdfs.convert-filesize"
#define CONFIG_FASTDFS_CONVERT_IMG_ENABLE "fastdfs.convert-enable"

typedef enum {
    IMAGE_PREFIX, IMAGE_HIGHT, IMAGE_WIDTH, IMAGE_EXT, IMAGE_ERR
} image_info_type;

/* plugin config for all request/connections */
typedef struct {
    unsigned short enable;
    array *filetypes;
    buffer *fdfs_conf;
    array *filesizes;
} plugin_config;

typedef struct {
    PLUGIN_DATA;
    buffer *range_buf;
    plugin_config **config_storage;
    plugin_config conf;

} plugin_data;

/* init the plugin data */
INIT_FUNC(mod_fastdfs_init) {
    plugin_data *p;
    p = calloc(1, sizeof (*p));
    p->range_buf = buffer_init();
    /* Initialize imagemagic. */
    MagickCoreGenesis(NULL, MagickTrue);
    return p;
}

/* detroy the plugin data */
FREE_FUNC(mod_fastdfs_free) {
    plugin_data *p = p_d;
    UNUSED(srv);
    if (!p)
        return HANDLER_GO_ON;
    if (p->config_storage) {
        size_t i;
        for (i = 0; i < srv->config_context->used; i++) {
            plugin_config *s = p->config_storage[i];
            array_free(s->filesizes);
            array_free(s->filetypes);
            buffer_free(s->fdfs_conf);
            free(s);
        }
        free(p->config_storage);
    }
    buffer_free(p->range_buf);
    /* Destroy imagemagic */
    MagickCoreTerminus();
    tracker_close_all_connections();
    fdfs_client_destroy();

    free(p);
    return HANDLER_GO_ON;
}

/* handle plugin config and check values */
SETDEFAULTS_FUNC(mod_fastdfs_set_defaults) {
    plugin_data *p = p_d;
    size_t i = 0;
    config_values_t cv[] = {
        { CONFIG_FASTDFS_CLIENT_CONF, NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_SERVER},
        { CONFIG_FASTDFS_CONVERT_IMG_ENABLE, NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION},
        { CONFIG_FASTDFS_CONVERT_IMG_TYPES, NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION},
        { CONFIG_FASTDFS_CONVERT_IMG_SIZE, NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION},
        { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET}
    };

    if (!p)
        return HANDLER_ERROR;
    p->config_storage = calloc(1, srv->config_context->used * sizeof (specific_config *));
    for (i = 0; i < srv->config_context->used; i++) {
        plugin_config *s;
        s = calloc(1, sizeof (plugin_config));
        s->enable = 1;
        s->filesizes = array_init();
        s->filetypes = array_init();
        s->fdfs_conf = buffer_init();
        cv[0].destination = s->fdfs_conf;
        cv[1].destination = &(s->enable);
        cv[2].destination = s->filetypes;
        cv[3].destination = s->filesizes;
        p->config_storage[i] = s;
        if (0 != config_insert_values_global(srv, ((data_config *) srv->config_context->data[i])->value, cv)) {
            return HANDLER_ERROR;
        }
    }
    /* fastdfs config file */
    p->conf.fdfs_conf = p->config_storage[0]->fdfs_conf;
    if (buffer_is_empty(p->conf.fdfs_conf)) {
        return HANDLER_ERROR;
    }
    log_init();
    g_log_context.log_level = LOG_ERR;
    int result = -1;
    if ((result = fdfs_client_init(p->conf.fdfs_conf->ptr)) != 0) {
        return HANDLER_ERROR;
    }

    return HANDLER_GO_ON;
}

#define PATCH_OPTION(x) p->conf.x = s->x;

static int mod_fastdfs_patch_connection(server *srv, connection *con, plugin_data *p) {
    size_t i, j;
    plugin_config *s = p->config_storage[0];
    PATCH_OPTION(filesizes);
    PATCH_OPTION(enable);
    PATCH_OPTION(filetypes);
    PATCH_OPTION(fdfs_conf);

    /* skip the first, the global context */
    for (i = 1; i < srv->config_context->used; i++) {
        data_config *dc = (data_config *) srv->config_context->data[i];
        s = p->config_storage[i];
        /* condition didn't match */
        if (!config_check_cond(srv, con, dc))
            continue;
        /* merge config */
        for (j = 0; j < dc->value->used; j++) {
            data_unset *du = dc->value->data[j];
            if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_FASTDFS_CONVERT_IMG_ENABLE))) {
                PATCH_OPTION(enable);
            } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_FASTDFS_CONVERT_IMG_SIZE))) {
                PATCH_OPTION(filesizes);
            } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_FASTDFS_CONVERT_IMG_TYPES))) {
                PATCH_OPTION(filetypes);
            }
        }
    }
    return 0;
}

#undef PATCH_OPTION

PHYSICALPATH_FUNC(mod_fastdfs_physical_handler) {
    plugin_data *p = p_d;
    if (con->http_status != 0)
        return HANDLER_GO_ON;
    if (con->uri.path->used == 0) {
        return HANDLER_GO_ON;
    }
    if (con->physical.path->used == 0) {
        return HANDLER_GO_ON;
    }
    if (con->mode != DIRECT) {
        return HANDLER_GO_ON;
    }
    if (con->file_finished)
        return HANDLER_GO_ON;
    mod_fastdfs_patch_connection(srv, con, p);
    con->mode = p->id;
    return HANDLER_GO_ON;
}

static char *get_file_id(buffer *uri) {
    char *file_id = NULL;
    if (uri->used && uri->ptr[0] == '/') {
        file_id = uri->ptr + 1;
    } else if (uri->used) {
        file_id = uri->ptr;
    }
    return file_id;
}

static int magic_resize(const char *mem, int length, buffer *buf, buffer *hw, buffer *file_ext, size_t quality) {
    int result = 0;

    ExceptionInfo *exception;
    Image *image, *images, *images1, *resize_image, *thumbnails;
    buffer *gif = buffer_init_string(".gif");
    int is_gif = 0;
    uint width, height;
    char *p;
    size_t len;
    ImageInfo *image_info;
    char *q;
    unsigned char *dstmem;

    do {
        if (NULL == mem || NULL == buf || NULL == hw || NULL == file_ext) {
            result = -1;
            break;
        }

        if (hw->used == 0) {
            result = -2;
            break;
        }

        p = hw->ptr + 1;
        q = strchr(p, '_');
        *q = '\0';
        height = atoi(p);
        p = q + 1;
        width = atoi(p);
        *q = '_';
        if (height <= 0 || width <= 0) {
            result = -3;
            break;
        }

        if (file_ext->used == 0) {
            result = -4;
            break;
        } else {
            if (buffer_is_equal_right_len(file_ext, gif, gif->used - 1))
                is_gif = 1;
        }

        image_info = CloneImageInfo((ImageInfo *) NULL);
        exception = AcquireExceptionInfo();
        images = BlobToImage(image_info, mem, length, exception);
        if (exception->severity != UndefinedException) {
            CatchException(exception);
        }

        if (images == (Image *) NULL) {
            result = -5;
            break;
        }

        if (images->columns <= width || images->rows <= height) {
            DestroyImage(images);
            result = -6;
            break;
        }

        if (is_gif) {
            images1 = CoalesceImages(images, exception);
            DestroyImage(images);
            images = images1;

            thumbnails = NewImageList();
            while ((image = RemoveFirstImageFromList(&images)) != (Image *) NULL) {
                resize_image = ResizeImage(image, height, width, LanczosFilter, 1.0, exception);
                if (resize_image == (Image *) NULL) {
                    MagickError(exception->severity, exception->reason, exception->description);
                }

                resize_image->quality = quality;

                (void) AppendImageToList(&thumbnails, resize_image);
                DestroyImage(image);
            }
            images1 = OptimizeImageLayers(thumbnails, exception);
            DestroyImage(thumbnails);
            thumbnails = images1;
        } else {
            thumbnails = ResizeImage(images, height, width, LanczosFilter, 1.0, exception);
            if (thumbnails == (Image *) NULL) {
                MagickError(exception->severity, exception->reason, exception->description);
            }
            thumbnails->quality = quality;
            dstmem = ImageToBlob(image_info, thumbnails, &len, exception);
        }
        dstmem = ImagesToBlob(image_info, thumbnails, &len, exception);
        if (dstmem == NULL) {
            thumbnails = DestroyImageList(thumbnails);
            image_info = DestroyImageInfo(image_info);
            exception = DestroyExceptionInfo(exception);
            result = -7;
            break;
        }
        buffer_prepare_copy(buf, len + 1);
        (void) memcpy(buf->ptr, dstmem, len);
        buf->used = len + 1;
        *(buf->ptr + len) = '\0';
        result = len;

        RelinquishMagickMemory(dstmem);
        thumbnails = DestroyImageList(thumbnails);
        image_info = DestroyImageInfo(image_info);
        exception = DestroyExceptionInfo(exception);

        break;
    } while (0);
    buffer_free(gif);
    return result;
}

static int http_response_parse_range(server *srv, connection *con, plugin_data *p, buffer *content) {
    int multipart = 0;
    int error;
    off_t start, end;
    const char *s, *minus;
    char *boundary = "fkj49sn38dcn3";
    data_string *ds;
    buffer *content_type = NULL;
    start = 0;
    end = content->used - 1;
    con->response.content_length = 0;
    if (NULL != (ds = (data_string *) array_get_element(con->response.headers, "Content-Type"))) {
        content_type = ds->value;
    }
    for (s = con->request.http_range, error = 0; !error && *s && NULL != (minus = strchr(s, '-'));) {
        char *err;
        off_t la, le;

        if (s == minus) {
            /* -<stop> */
            le = strtoll(s, &err, 10);
            if (le == 0) {
                /* RFC 2616 - 14.35.1 */
                con->http_status = 416;
                error = 1;
            } else if (*err == '\0') {
                /* end */
                s = err;
                end = content->used - 1;
                start = content->used + le;
            } else if (*err == ',') {
                multipart = 1;
                s = err + 1;
                end = content->used - 1;
                start = content->used + le;
            } else {
                error = 1;
            }
        } else if (*(minus + 1) == '\0' || *(minus + 1) == ',') {
            /* <start>- */
            la = strtoll(s, &err, 10);
            if (err == minus) {
                /* ok */
                if (*(err + 1) == '\0') {
                    s = err + 1;
                    end = content->used - 1;
                    start = la;
                } else if (*(err + 1) == ',') {
                    multipart = 1;
                    s = err + 2;
                    end = content->used - 1;
                    start = la;
                } else {
                    error = 1;
                }
            } else {
                /* error */
                error = 1;
            }
        } else {
            /* <start>-<stop> */
            la = strtoll(s, &err, 10);
            if (err == minus) {
                le = strtoll(minus + 1, &err, 10);
                /* RFC 2616 - 14.35.1 */
                if (la > le) {
                    error = 1;
                }
                if (*err == '\0') {
                    /* ok, end*/
                    s = err;
                    end = le;
                    start = la;
                } else if (*err == ',') {
                    multipart = 1;
                    s = err + 1;
                    end = le;
                    start = la;
                } else {
                    /* error */
                    error = 1;
                }
            } else {
                /* error */
                error = 1;
            }
        }
        if (!error) {
            /* RFC 2616 - 14.35.1 */
            if (start < 0)
                start = 0;

            if (end > content->used - 1)
                end = content->used - 1;
            if (start > content->used - 1) {
                error = 1;
                con->http_status = 416;
            }
        }
        if (!error) {
            if (multipart) {
                /* write boundary-header */
                buffer *b;
                b = chunkqueue_get_append_buffer(con->write_queue);
                buffer_copy_string_len(b, CONST_STR_LEN("\r\n--"));
                buffer_append_string(b, boundary);
                /* write Content-Range */
                buffer_append_string_len(b, CONST_STR_LEN("\r\nContent-Range: bytes "));
                buffer_append_off_t(b, start);
                buffer_append_string_len(b, CONST_STR_LEN("-"));
                buffer_append_off_t(b, end);
                buffer_append_string_len(b, CONST_STR_LEN("/"));
                buffer_append_off_t(b, content->used);
                buffer_append_string_len(b, CONST_STR_LEN("\r\nContent-Type: "));
                buffer_append_string_buffer(b, content_type);
                /* write END-OF-HEADER */
                buffer_append_string_len(b, CONST_STR_LEN("\r\n\r\n"));
                con->response.content_length += b->used - 1;
            }
            chunkqueue_append_mem(con->write_queue, content->ptr + start, end - start + 1);
            con->response.content_length += end - start + 1;
        }
    }

    /* something went wrong */
    if (error)
        return -1;
    if (multipart) {
        /* add boundary end */
        buffer *b;
        b = chunkqueue_get_append_buffer(con->write_queue);
        buffer_copy_string_len(b, "\r\n--", 4);
        buffer_append_string(b, boundary);
        buffer_append_string_len(b, "--\r\n", 4);
        con->response.content_length += b->used - 1;
        /* set header-fields */
        buffer_copy_string_len(p->range_buf, CONST_STR_LEN("multipart/byteranges; boundary="));
        buffer_append_string(p->range_buf, boundary);
        /* overwrite content-type */
        response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(p->range_buf));
    } else {
        /* add Content-Range-header */
        buffer_copy_string_len(p->range_buf, CONST_STR_LEN("bytes "));
        buffer_append_off_t(p->range_buf, start);
        buffer_append_string_len(p->range_buf, CONST_STR_LEN("-"));
        buffer_append_off_t(p->range_buf, end);
        buffer_append_string_len(p->range_buf, CONST_STR_LEN("/"));
        buffer_append_off_t(p->range_buf, content->used);
        response_header_insert(srv, con, CONST_STR_LEN("Content-Range"), CONST_BUF_LEN(p->range_buf));
    }
    /* ok, the file is set-up */

    return 0;
}

static int is_numeric(char *str, int len) {
    if (!str)
        return 0;
    int i;
    char c;
    for (i = 0; i < len; i++) {
        c = *(str + i);
        if (!isdigit(c))
            return 0;
    }
    return 1;
}

static char *find_chr_from_right(const char *str, const char *start, int ch) {
    while (--start != str && *start != (char) ch);
    if (*start == (char) ch) {
        return ((char *) start);
    }
    return NULL;
}

static char *can_convert(const plugin_data *p, const char *path, int *width, int *height, int *quality, char *ext) {
    data_string *ds;
    int length = 0, i = 0;
    char *start = NULL, *end = NULL;
    char param0[10] = {0}, param1[10] = {0}, param2[10] = {0};
    if ((start = strrchr(path, '.')) == NULL)
        return NULL;
    length = path + strlen(path) - start;
    if (sizeof (ext) <= length)
        return NULL;
    snprintf(ext, length, "%s", start);
    for (i = 0; i < p->conf.filetypes->used; i++) {
        ds = (data_string *) p->conf.filetypes->data[i];
        if (ds->value->used == 0)
            continue;
        if (strncasecmp(ext, ds->value, ds->value->used) == 0)
            goto file_type_found;
    }
    return NULL;
file_type_found:
    end = start;
    if ((start = strrchr(path, '_')) == NULL)
        return NULL;
    length = path + strlen(path) - ds->value->used - start - 1;
    if (sizeof (param0) <= length)
        return NULL;
    snprintf(param0, length, "%s", start + 1);
    if (0 == is_numeric(param0, strlen(param0)))
        return NULL;
    end = start;
    if ((start = find_chr_from_right(path, end, '_')) == NULL)
        return NULL;
    length = end - start - 1;
    if (sizeof (param1) <= length)
        return NULL;
    snprintf(param1, length, "%s", start + 1);
    if (0 == is_numeric(param1, strlen(param1)))
        return NULL;
    end = start;
    if ((start = find_chr_from_right(path, end, '_')) != NULL) {
        length = end - start - 1;
        if (sizeof (param2) <= length)
            return NULL;
        snprintf(param2, length, "%s", start + 1);
        if (1 == is_numeric(param2, strlen(param2))) {
            *width = atoi(param2);
            *height = atoi(param1);
            *quality = atoi(param0);
            buffer *tmp = buffer_init();
            buffer_append_string(tmp, "_");
            buffer_append_string_buffer(tmp, param2);
            buffer_append_string(tmp, "_");
            buffer_append_string_buffer(tmp, param1);
            for (i = 0; i < p->conf.filesizes->used; i++) {
                ds = (data_string *) p->conf.filesizes->data[i];
                if (strcasecmp(ds->value->ptr, "*") == 0)
                    return start;
                if (strcasecmp(ds->value->ptr, tmp->ptr) == 0)
                    return start;
            }
        }
    }
    *width = atoi(param1);
    *height = atoi(param0);
    *quality = 1;
    buffer *tmp = buffer_init();
    buffer_append_string(tmp, "_");
    buffer_append_string_buffer(tmp, param1);
    buffer_append_string(tmp, "_");
    buffer_append_string_buffer(tmp, param0);
    for (i = 0; i < p->conf.filesizes->used; i++) {
        ds = (data_string *) p->conf.filesizes->data[i];
        if (strcasecmp(ds->value->ptr, "*") == 0)
            return end;
        if (strcasecmp(ds->value->ptr, tmp->ptr) == 0)
            return end;
    }
    return NULL;
}

/* 获取文件名中的参数 */
static int get_filename_params(array *params, const buffer *filename){
	int i;
	buffer *tmp;
	char *start = NULL;
	char *end = NULL;
	data_string *param;
	tmp = buffer_init();
	buffer_copy_memory(tmp, filename->ptr, filename->used + 1);

	array_set_key_value(params, "height", CONST_STR_LEN("height"), "", CONST_STR_LEN(""));
	array_set_key_value(params, "width", CONST_STR_LEN("width"), "", CONST_STR_LEN(""));
	array_set_key_value(params, "quality", CONST_STR_LEN("quality"), "", CONST_STR_LEN(""));

	if ((flag = strrchr(tmp->ptr, '.')) != NULL) {
		*end = '\0';
	}

	for (i = 0; i < params->used; i++) {
		if ((start = strrchr(tmp->ptr, '_')) != NULL) {
			param = (data_string *) params->data[i];
			buffer_reset(param->value);
			buffer_copy_string_len(param->value, flag, end - start);
			end = start;
			*end = '\0';
		} else {
			break;
		}
	}
	buffer_free(tmp);

	return 0;
}

/* 获取url的请求参数 */
static int get_url_params(array *params, buffer *url) {
	size_t is_key = 1;
	size_t i;
	char *key = NULL, *val = NULL;
	key = url->ptr;
	for (i = 0; i < url->used; i++) {
		switch(url->ptr[i]) {
		case '=':
			if (is_key) {
				val = url->ptr + i + 1;
				url->ptr[i] = '\0';
				is_key = 0;
			}
			break;
		case '&':
		case '\0': /* fin symbol */
			if (!is_key) {
				data_string *ds;
				url->ptr[i] = '\0';
				if (NULL == (ds = (data_string *)array_get_unused_element(params, TYPE_STRING))) {
					ds = data_string_init();
				}
				buffer_copy_string_len(ds->key, key, strlen(key));
				buffer_copy_string_len(ds->value, val, strlen(val));

				array_insert_unique(params, (data_unset *)ds);
			}
			key = url->ptr + i + 1;
			val = NULL;
			is_key = 1;
			break;
		}
	}

	return 0;
}

#define EXITFUNC 		buffer_reset(con->physical.path);\
						buffer_free(orgial_path);\
						buffer_free(file_hw);\
						con->mode = DIRECT;\
						buffer_free(img_content);\
						buffer_free(file_ext);\
						return HANDLER_FINISHED\


SUBREQUEST_FUNC(mod_fastdfs_subrequest) {

    plugin_data *p = p_d;


    data_string *ds;
    data_string *filesize;
    char resize_file_id[512];
    buffer *orgial_path = NULL;
    buffer *img_content = NULL;
    buffer *file_hw = NULL;
    buffer *mtime = NULL;
    char *file_id;


    /* someone else has done a decision for us */
    if (con->http_status != 0)
        return HANDLER_GO_ON;
    if (con->uri.path->used == 0)
        return HANDLER_GO_ON;
    if (con->physical.path->used == 0)
        return HANDLER_GO_ON;
    /* someone else has handled this request */
    if (con->mode != DIRECT && con->mode != p->id)
        return HANDLER_GO_ON;
    if (con->file_finished)
        return HANDLER_GO_ON;
    /* we only handle GET, POST and HEAD */
    switch (con->request.http_method) {
        case HTTP_METHOD_GET:
		case HTTP_METHOD_POST:
		case HTTP_METHOD_HEAD:
            break;
        default:
            return HANDLER_GO_ON;
    }

    mod_fastdfs_patch_connection(srv, con, p);


    int is_convert = 0;
    size_t k, j;
    char *ext = NULL;
    orgial_path = buffer_init();
    file_hw = buffer_init();
    img_content = buffer_init();
    buffer *file_ext = buffer_init();
    char *slash0 = NULL;
    char height[20];
    char width[20];

    char param0[20], param1[20], param2[20];
    char *slash1 = NULL;
    int is_break = 0;
    size_t quality = 100;
    char *slash2 = NULL;

    memset(width, 0, sizeof (width));
    memset(height, 0, sizeof (height));

    int is_quality = 0;
    
    if (HANDLER_FINISHED == http_response_handle_cachable(srv, con, mtime)) {
        log_error_write(srv, __FILE__, __LINE__, "s", "(debug) unknown state10");
        buffer_free(content_type);
        buffer_reset(con->physical.path);
        buffer_free(orgial_path);
        buffer_free(file_hw);
        buffer_free(img_content);
        buffer_free(file_ext);
        return HANDLER_FINISHED;
    }

    for (k = 0; k < p->conf.filesizes->used; k++) {
        filesize = (data_string *) p->conf.filesizes->data[k];
        if (filesize->value->used == 0)
            continue;
        if (strcasecmp(filesize->value->ptr, "*") == 0) {
            for (j = 0; j < p->conf.filetypes->used; j++) {
                ds = (data_string *) p->conf.filetypes->data[j];
                if (ds->value->used == 0)
                    continue;
                if (buffer_is_equal_right_len(con->physical.rel_path, ds->value, ds->value->used - 1)) {
                    //从这里开始解析我们的后缀参数：[高度_宽度_质量.EXT]
                    ext = ds->value->ptr + 1;
                    buffer *path = con->physical.rel_path;
                    //从最后开始获取，存入参数3
                    if ((slash0 = strrchr(path->ptr, '_')) != NULL) {
                        *(path->ptr + path->used - ds->value->used) = '\0';
                        memset(param0, 0, sizeof (param0));
                        snprintf(param0, sizeof (param0), "%s", slash0 + 1);
                        if (0 == is_numeric(param0, strlen(param0))) {
                            is_convert = 0;
                            is_break = 1;
                            *(path->ptr + path->used - ds->value->used) = '.';
                            break;
                        }
                        *(path->ptr + path->used - ds->value->used) = '.';
                        *slash0 = '\0';

                        //开始获取第二个参数,存入参数2
                        if ((slash1 = strrchr(path->ptr, '_')) != NULL) {
                            memset(param1, 0, sizeof (param1));
                            snprintf(param1, sizeof (param1), "%s", slash1 + 1);
                            if (0 == is_numeric(param1, strlen(param1))) {
                                is_convert = 0;
                                is_break = 1;
                                *slash0 = '_';
                                break;
                            }
                            *slash1 = '\0';

                            if (strlen(path->ptr) > 48 && (slash2 = strrchr(path->ptr, '_')) != NULL) {
                                memset(param2, 0, sizeof (param2));
                                snprintf(param2, sizeof (param2), "%s", slash2 + 1);
                                if (0 == is_numeric(param2, strlen(param2))) {
                                    is_convert = 0;
                                    is_break = 1;
                                    *slash0 = '_';
                                    *slash1 = '_';
                                    break;
                                }

                                *slash0 = '_';
                                *slash1 = '_';
                                is_convert = 1;
                                buffer_copy_string(file_ext, ds->value->ptr);
                                buffer_copy_string(orgial_path, path->ptr);
                                buffer_append_string_buffer(orgial_path, ds->value);
                                *(path->ptr + path->used - ds->value->used) = '\0';
                                buffer_copy_string(file_hw, slash2);
                                *(path->ptr + path->used - ds->value->used) = '.';
                                is_quality = 1;
                                break;
                            } else {
                                is_convert = 0;
                                is_break = 1;
                                *slash0 = '_';
                                is_convert = 1;
                                buffer_copy_string(file_ext, ds->value->ptr);
                                buffer_copy_string(orgial_path, path->ptr);
                                buffer_append_string_buffer(orgial_path, ds->value);
                                *(path->ptr + path->used - ds->value->used) = '\0';
                                buffer_copy_string(file_hw, slash1);
                                *(path->ptr + path->used - ds->value->used) = '.';
                                is_quality = 0;
                                break;
                            }
                        } else {
                            is_convert = 0;
                            is_break = 1;
                            *slash0 = '_';
                            break;
                        }
                    } else {
                        is_convert = 0;
                        is_break = 1;
                        break;
                    }
                }
            }
        } else {
            for (j = 0; j < p->conf.filetypes->used; j++) {
                ds = (data_string *) p->conf.filetypes->data[j];
                if (ds->value->used == 0)
                    continue;
                buffer_copy_string_buffer(file_ext, filesize->value);
                buffer_append_string_buffer(file_ext, ds->value);
                if (buffer_is_equal_right_len(con->physical.rel_path, file_ext, file_ext->used - 1)) {
                    ext = ds->value->ptr + 1;
                    buffer_copy_string_len(orgial_path, con->physical.rel_path->ptr, con->physical.rel_path->used - file_ext->used);
                    buffer_append_string_buffer(orgial_path, ds->value);
                    is_convert = 1;
                    break;
                }
            }
        }
        if (is_convert == 1 || is_break == 1)
            break;
    }

    if (is_quality == 1) {
        quality = atoi(param0);
        strcpy(height, param1);
        strcpy(width, param2);
    } else {
        quality = 100;
        strcpy(height, param0);
        strcpy(width, param1);
    }


    if (is_convert && p->conf.enable) {
        if (file_hw->used == 0)
            buffer_copy_string_buffer(file_hw, ((data_string *) p->conf.filesizes->data[k])->value);
    }
    TrackerServerInfo *pTrackerServer = tracker_get_connection();
    if (pTrackerServer == NULL) {
        con->http_status = 500;
        EXITFUNC;
    }
    file_id = get_file_id(con->physical.rel_path);
    char *new_file_id = get_file_id(orgial_path);
    FDFSFileInfo file_info;
    char *file_buf = NULL;
    int64_t file_size;
    int result;
    log_error_write(srv, __FILE__, __LINE__, "s", "(debug) unknown state1");
    if (0 != (result = storage_download_file1(pTrackerServer, NULL, file_id, &file_buf, &file_size))) {
        log_error_write(srv, __FILE__, __LINE__, "s", "(debug) unknown state2");
        if (is_convert && p->conf.enable) {
            if (0 != (result = storage_download_file1(pTrackerServer, NULL, new_file_id, &file_buf, &file_size))) {
                if (result == ENOENT) {
                    con->http_status = 404;
                } else {
                    con->http_status = 500;
                }
                EXITFUNC;
            }
            log_error_write(srv, __FILE__, __LINE__, "s", "(debug) unknown state3");
            if ((result = magic_resize(file_buf, file_size, img_content, file_hw, file_ext, quality)) > 0) {
                log_error_write(srv, __FILE__, __LINE__, "s", "(debug) unknown state4");
                if (ext) {
                    storage_upload_slave_by_filebuff1(pTrackerServer, NULL, img_content->ptr, img_content->used, new_file_id, file_hw->ptr, ext, NULL, 0, resize_file_id);

                    if ((result = fdfs_get_file_info1(file_id, &file_info)) != 0) {
                        if (result == ENOENT) {
                            con->http_status = 404;
                        } else {
                            con->http_status = 500;
                        }
                        EXITFUNC;
                    }

                }
            } else {
                log_error_write(srv, __FILE__, __LINE__, "s", "(debug) unknown state5");
                buffer_free(img_content);
                img_content = buffer_init();
                buffer_prepare_copy(img_content, file_size + 1);
                (void) memcpy(img_content->ptr, file_buf, file_size);
                img_content->used = file_size + 1;
                *(img_content->ptr + file_size) = '\0';
            }
            free(file_buf);
        } else {
            con->http_status = 404;
            EXITFUNC;
        }
    } else {
        log_error_write(srv, __FILE__, __LINE__, "s", "(debug) unknown state6");
        result = fdfs_get_file_info1(file_id, &file_info);
        buffer_prepare_copy(img_content, file_size + 1);
        (void) memcpy(img_content->ptr, file_buf, file_size);
        img_content->used = file_size + 1;
        *(img_content->ptr + file_size) = '\0';
        free(file_buf);
    }
    log_error_write(srv, __FILE__, __LINE__, "s", "(debug) unknown state7");


    /* get request file type */
    buffer *content_type;
    content_type = buffer_init();
    for (k = 0; k < con->conf.mimetypes->used; k++) {
        ds = (data_string *) con->conf.mimetypes->data[k];
        buffer *type = ds->key;

        if (type->used == 0)
            continue;
        if (type->used > con->physical.path->used)
            continue;

        if (0 == strncasecmp(con->physical.path->ptr + con->physical.path->used - type->used, type->ptr, type->used - 1)) {
            buffer_copy_string_buffer(content_type, ds->value);
            break;
        }
    }

    /* set response content-type, if not set already */
    if (NULL == array_get_element(con->response.headers, "Content-Type")) {
        if (buffer_is_empty(content_type)) {
            /* we are setting application/octet-stream, but also announce that
             * this header field might change in the seconds few requests 
             *
             * This should fix the aggressive caching of FF and the script download
             * seen by the first installations
             */
            response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("application/octet-stream"));
        } else {
            response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(content_type));
        }
    }
    /* prepare header */
    if (NULL == (ds = (data_string *) array_get_element(con->response.headers, "Last-Modified"))) {
        log_error_write(srv, __FILE__, __LINE__, "s", "(debug) unknown state8");
        mtime = strftime_cache_get(srv, file_info.create_timestamp);
        response_header_overwrite(srv, con, CONST_STR_LEN("Last-Modified"), CONST_BUF_LEN(mtime));
    } else {
        mtime = ds->value;
    }
    if (NULL == array_get_element(con->response.headers, "ETag")) {
        log_error_write(srv, __FILE__, __LINE__, "s", "(debug) unknown state9");
        /* generate e-tag */
        buffer *file_etag = buffer_init();
        buffer_append_long(file_etag, file_info.file_size);
        buffer_append_string_buffer(file_etag, mtime);
        etag_mutate(con->physical.etag, file_etag);
        buffer_free(file_etag);
        response_header_overwrite(srv, con, CONST_STR_LEN("ETag"), CONST_BUF_LEN(con->physical.etag));
    }

    if (con->request.http_range && con->conf.range_requests) {
        log_error_write(srv, __FILE__, __LINE__, "s", "(debug) unknown state11");
        int do_range_request = 1;
        /* check if we have a conditional GET */
        if (NULL != (ds = (data_string *) array_get_element(con->request.headers, "If-Range"))) {
            log_error_write(srv, __FILE__, __LINE__, "s", "(debug) unknown state12");
            /* if the value is the same as our ETag, we do a Range-request,otherwise a full 200 */
            if (ds->value->ptr[0] == '"') {
                /* client wants a ETag */
                if (!con->physical.etag) {
                    do_range_request = 0;
                } else if (!buffer_is_equal(ds->value, con->physical.etag)) {
                    do_range_request = 0;
                }
            } else if (!mtime) {
                /* we don't have a Last-Modified and can match the If-Range:sending all */
                do_range_request = 0;
            } else if (!buffer_is_equal(ds->value, mtime)) {
                do_range_request = 0;
            }
        }

        if (do_range_request) {
            log_error_write(srv, __FILE__, __LINE__, "s", "(debug) unknown state13");
            /* content prepared, I'm done */
            con->file_finished = 1;
            /* break point resume */
            if (0 == http_response_parse_range(srv, con, p, img_content)) {
                con->http_status = 206;
            }
            buffer_free(content_type);
            buffer_reset(con->physical.path);
            buffer_free(orgial_path);
            buffer_free(file_hw);
            buffer_free(img_content);
            buffer_free(file_ext);
            return HANDLER_FINISHED;
        }
    }
    log_error_write(srv, __FILE__, __LINE__, "s", "(debug) unknown state14");
    buffer_free(content_type);
    chunkqueue_append_buffer(con->write_queue, img_content);
    buffer_reset(con->physical.path);
    buffer_free(orgial_path);
    buffer_free(file_hw);
    buffer_free(img_content);
    buffer_free(file_ext);
    con->file_finished = 1;
    con->mode = DIRECT;
    return HANDLER_FINISHED;
}

#undef EXITFUNC

int mod_fastdfs_plugin_init(plugin *p);

int mod_fastdfs_plugin_init(plugin *p) {
    p->version = LIGHTTPD_VERSION_ID;
    p->name = buffer_init_string("fastdfs");

    p->init = mod_fastdfs_init;
    p->handle_physical = mod_fastdfs_physical_handler;
    p->handle_subrequest = mod_fastdfs_subrequest;

    p->set_defaults = mod_fastdfs_set_defaults;
    p->cleanup = mod_fastdfs_free;

    p->data = NULL;

    return 0;
}
